/*
 * Copyright 2013 Pavel Stastny <pavel.stastny at gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.aplikator.server.processes.impl.processes;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.SerializationUtils;
import org.aplikator.server.processes.ProcessState;
import org.aplikator.server.processes.Work;
import org.aplikator.server.processes.impl.WorksChain;
import org.aplikator.server.processes.impl.processes.ipc.IPCChanelFactory;
import org.aplikator.server.processes.impl.processes.ipc.IPCChannel;
import org.aplikator.server.processes.impl.processes.ipc.IPCMessage;
import org.aplikator.server.processes.impl.processes.ipc.impl.IPCMessageImpl;
import org.aplikator.server.util.IOUtils;

/**
 *
 * @author Pavel Stastny <pavel.stastny at gmail.com>
 */
public class ProcessStarter {

    public static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(ProcessStarter.class.getName());
    
    /** logging property */
    public static final String LOGGING_FILE_PROPERTY = "java.util.logging.config.file";
    public static final String LOGGING_CLASS_PROPERTY = "java.util.logging.config.class";
    
    /** Runnable serialized class */
    public static final String RUNNABLE_STREAM_FILE ="runnable";
    /** Works serialized */
    public static final String WORKS_STREAM_FILES ="works";
    
    /** CLASSPATH */
    public static final String CLASSPATH = "CLASSPATH";

    /** process identifier */
    public static final String UUID="uuid";
    
    /** App name */
    public static final String APP_NAME="appname";
    
    public static final String SOUT_FILE = "SOUT";
    public static final String SERR_FILE = "SERR";
    private static boolean STATUS_UPDATED = false;
    
    
    public static void main(String[] args)  {
        PrintStream outStream = null;
        PrintStream errStream = null;
        try {
            System.out.println("Updating status");
            updateStatus(ProcessState.RUNNING);
            System.out.println("Updating pid");
            updatePID();
            System.out.println("Redirecting output");
            if (System.getProperty(SOUT_FILE) != null) {
                outStream = createPrintStream(System.getProperty(SOUT_FILE));
                System.setOut(outStream);
            }
            if (System.getProperty(SERR_FILE) != null) {
                errStream = createPrintStream(System.getProperty(SERR_FILE));
                System.setErr(errStream);
            }
            setDefaultLoggingIfNecessary();

            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    if (!STATUS_UPDATED) {
                        //updateStatus(States.KILLED);
                    }
                }
            });


            if (System.getProperties().containsKey(RUNNABLE_STREAM_FILE)) {
                String fpath = System.getProperty(RUNNABLE_STREAM_FILE);
                Object deserialized = deserialization(fpath);
                // no thread 
                ((Runnable)deserialized).run();
            } else {
                String f = System.getProperty(WORKS_STREAM_FILES);
                String[] files = f.split(File.pathSeparator);
                Work[] works = new Work[files.length];
                for (int i = 0; i < files.length; i++) {
                    works[i] = (Work) deserialization(files[i]);
                }
                // no thread start
                new WorksChain(works).run();
            }


            updateStatus(ProcessState.FINISHED);
        } catch (Throwable e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            try {
                updateStatus(ProcessState.FAILED);
            } catch (IOException ex) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            } catch (ClassNotFoundException ex) {
                LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            } catch (InstantiationException ex) {
                LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            } catch (IllegalAccessException ex) {
                LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            }
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (Exception e1) {
                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
                }
            }
            if (errStream != null) {
                try {
                    errStream.close();
                } catch (Exception e1) {
                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
                }
            }
        } finally {
//            String uuid = System.getProperty(ProcessStarter.UUID_KEY);
//            String closeTokenFlag = System.getProperty(AUTOMATIC_CLOSE_TOKEN, "true");
//            if (closeTokenFlag != null && closeTokenFlag.trim().toLowerCase().equals("true")) {
//                ProcessUtils.closeToken(uuid);
//            }
        }
    }

    private static void setDefaultLoggingIfNecessary() {
//        String classProperty = System.getProperty(LOGGING_CLASS_PROPERTY);
//        String fileProperty = System.getProperty(LOGGING_FILE_PROPERTY);
//        if ((classProperty == null) && (fileProperty == null)) {
//            // loads default logging 
//            new LoggingLoader();
//        }
    }

    private static PrintStream createPrintStream(String file) throws FileNotFoundException {
        return new PrintStream(new FileOutputStream(file));
    }

    public static void updateStatus(ProcessState state) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        String uuid = System.getProperty(UUID);
        String appname = System.getProperty(APP_NAME);
        IPCMessage message = new IPCMessageImpl(uuid, "1", "{'action':'updateState','value':'"+state.name()+"'}");
        IPCChannel channel = IPCChanelFactory.createFactory().createChannel(appname);
        channel.sendMessage(message);
    }

    
    public static void updatePID() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        String uuid = System.getProperty(UUID);
        String appname = System.getProperty(APP_NAME);
        IPCMessage message = new IPCMessageImpl(uuid, "1", "{'action':'updatePid','value':"+getPID()+"}");
        IPCChannel channel = IPCChanelFactory.createFactory().createChannel(appname);
        channel.sendMessage(message);
    }
    


    /**
     * Returns PID of process
     *
     * @return
     */
    public static String getPID() {
        String pid = null;
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String[] split = name.split("@");
        if ((split != null) && (split.length > 1)) {
            pid = split[0];
        }
        return pid;
    }

    public static Method mainMethod(Class<?> clz) throws NoSuchMethodException {
        Method mainMethod = clz.getMethod("main", (new String[0]).getClass());
        return mainMethod;
    }

    public static String defaultParam(String[] defaultParams, int i) {
        return defaultParams.length > i ? defaultParams[i] : null;
    }

    private static Object deserialization(String fpath) throws IOException {
        File f = new File(fpath);
        byte[] bytes = IOUtils.readBytes(new FileInputStream(f), true);
        Object deserialized = SerializationUtils.deserialize(bytes);
        return deserialized;
    }
}
